<template>
  <div class="login-container">
    <!-- 3D粒子背景 -->
    <div id="particles-container" ref="particlesContainer" class="particles-background" />
    
    <div class="login-box">
      <div class="login-header">
        <div class="logo">
          <div class="logo-icon">
            <img src="/logo.svg" alt="Convallaria">
          </div>
          <h1>Convallaria</h1>
        </div>
        <p class="subtitle">企业级管理平台</p>
        <div class="header-line" />
      </div>
      
      <el-form
        ref="loginFormRef"
        :model="loginForm"
        :rules="loginRules"
        class="login-form"
      >
        <el-form-item prop="username" class="form-item-enhanced">
          <div class="input-wrapper" style="width: 100%">
            <el-input
              v-model="loginForm.username"
              placeholder="请输入用户名"
              size="large"
              :prefix-icon="User"
              clearable
              @keyup.enter="validateUsername"
              @focus="handleInputFocus"
              @blur="handleInputBlur"
            />
            <div class="input-line" />
          </div>
        </el-form-item>
        
        <el-form-item prop="password" class="form-item-enhanced">
          <div class="input-wrapper" style="width: 100%">
            <el-input
              v-model="loginForm.password"
              type="password"
              placeholder="请输入密码"
              size="large"
              :prefix-icon="Lock"
              show-password
              clearable
              @keyup.enter="validatePassword"
              @focus="handleInputFocus"
              @blur="handleInputBlur"
            />
            <div class="input-line" />
          </div>
        </el-form-item>
        
        <!-- 验证码输入框 - 根据配置显示/隐藏 -->
        <el-form-item v-if="isCaptchaEnabled" prop="captcha" class="form-item-enhanced">
          <div class="captcha-container">
            <div class="input-wrapper">
              <el-input
                v-model="loginForm.captcha"
                placeholder="请输入验证码"
                size="large"
                :prefix-icon="Picture"
                clearable
                :maxlength="captchaLength"
                show-word-limit
                @keyup.enter="handleCaptchaEnter"
                @input="handleCaptchaInput"
                @focus="handleInputFocus"
                @blur="handleInputBlur"
              />
              <div class="input-line" />
            </div>
            <div class="captcha-image" @click="refreshCaptcha">
              <img v-if="captchaImage && !captchaLoading" :src="captchaImage" alt="验证码">
              <el-icon v-else-if="captchaLoading" class="is-loading">
                <Loading />
              </el-icon>
              <span v-else>点击获取验证码</span>
            </div>
          </div>
        </el-form-item>
        
        <el-form-item v-if="isRememberMeEnabled">
          <div class="login-options">
            <el-checkbox v-model="loginForm.rememberMe" @change="handleRememberMe">记住我</el-checkbox>
            <el-link type="primary" :underline="false">忘记密码？</el-link>
          </div>
        </el-form-item>
        
        <el-form-item>
          <div class="button-wrapper">
            <el-button
              type="primary"
              size="large"
              class="login-button"
              :loading="loading"
              @click="handleLogin"
            >
              <span class="button-text">{{ loading ? '登录中...' : '登录' }}</span>
              <div class="button-shine" />
            </el-button>
          </div>
        </el-form-item>
      </el-form>
    </div>
    
    <div class="login-footer">
      <p>&copy; 2024 Convallaria. All rights reserved.</p>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { User, Lock, Picture, Loading } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
import { getCaptchaApi } from '@/api/user'
import type { LoginParams } from '@/types/user'
import { useCaptchaSettings, useLoginSettings, useSecuritySettings } from '@/composables/useUnifiedSettings'
import * as THREE from 'three'

const router = useRouter()
const userStore = useUserStore()

// 使用统一设置
const { isCaptchaEnabled, captchaLength, refreshOnError } = useCaptchaSettings()
const { isRememberMeEnabled } = useLoginSettings()
const { passwordMinLength, passwordMaxLength } = useSecuritySettings()

const loginFormRef = ref<FormInstance>()
const loading = ref(false)
const captchaImage = ref('')
const captchaKey = ref('')
const captchaLoading = ref(false)

// 3D粒子系统相关变量
const particlesContainer = ref<HTMLElement>()
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let particlesMesh: THREE.Points
let particlesGeometry: THREE.BufferGeometry
let particlesMaterial: THREE.ShaderMaterial
let lineMaterial: THREE.ShaderMaterial
let animationId: number

// 粒子系统参数
const getParticlesCount = () => {
  const isMobile = window.innerWidth < 768
  const isLowEnd = navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4
  if (isMobile) return 100
  if (isLowEnd) return 120
  return 200
}
const particlesCount = getParticlesCount()
const posArray = new Float32Array(particlesCount * 3)
const velArray = new Float32Array(particlesCount * 3)
const colorArray = new Float32Array(particlesCount * 3)
const range = 50 // 增大分布范围
const initSpeed = 0.008 // 减慢初始速度

// 交互变量
let mousePos = new THREE.Vector3()
let mouseActivity = 0
let mouseDown = false
let clickActivity = 0
let dragStart = false
const lastMousePos = new THREE.Vector2()
const keys = { ArrowUp: false, ArrowDown: false, ArrowLeft: false, ArrowRight: false }

// 爆炸效果变量
let explosionPos = new THREE.Vector3()
let explosionPower = 0

// 新交互效果变量
let mouseTrail: Array<{pos: THREE.Vector3, life: number}> = []
let ripples: Array<{pos: THREE.Vector3, power: number, radius: number}> = []
let gravityPoints: Array<{pos: THREE.Vector3, power: number, life: number}> = []
let targetShape: THREE.Vector3[] | null = null
let formingShape = false
let currentShapeType: string | null = null

// 粒子重新分布计时器
let redistributionTimer = 0
const redistributionInterval = 30000 // 30秒重新分布一次

// 登录表单
const loginForm = reactive<LoginParams>({
  username: '',  // 修改为与后端一致
  password: '',
  captcha: '',
  captchaKey: '',
  rememberMe: false
})

// 表单验证规则 - 根据验证码配置动态调整
const loginRules = computed<FormRules>(() => {
  const rules: FormRules = {
    username: [  // 修改为与后端一致
      { required: true, message: '请输入用户名', trigger: 'blur' },
      { min: 2, max: 20, message: '用户名长度在 2 到 20 个字符', trigger: 'blur' },
      { 
        pattern: /^[a-zA-Z0-9_]{2,20}$/, 
        message: '用户名只能包含字母、数字和下划线', 
        trigger: 'blur' 
      }
    ],
    password: [
      { required: true, message: '请输入密码', trigger: 'blur' },
      { 
        min: passwordMinLength.value, 
        max: passwordMaxLength.value, 
        message: `密码长度在 ${passwordMinLength.value} 到 ${passwordMaxLength.value} 个字符`, 
        trigger: 'blur' 
      },
      {
        pattern: new RegExp(`^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z\\d@$!%*?&]{${passwordMinLength.value},${passwordMaxLength.value}}$`),
        message: '密码必须包含字母和数字',
        trigger: 'blur'
      }
    ]
  }
  
  // 只有在启用验证码时才添加验证码验证规则
  if (isCaptchaEnabled.value) {
    rules.captcha = [
      { required: true, message: '请输入验证码', trigger: 'blur' },
      { len: captchaLength.value, message: `验证码长度为 ${captchaLength.value} 位`, trigger: 'blur' },
      { 
        pattern: new RegExp(`^[a-zA-Z0-9]{${captchaLength.value}}$`), 
        message: '验证码只能包含字母和数字', 
        trigger: 'blur' 
      }
    ]
  }
  
  return rules
})

// 获取验证码
const refreshCaptcha = async () => {
  try {
    captchaLoading.value = true
    const response = await getCaptchaApi()
    // 修复：后端返回的是 ApiResult<CaptchaDTO>，需要访问 response.data
    captchaImage.value = response.captchaImage
    captchaKey.value = response.captchaKey
    loginForm.captchaKey = response.captchaKey
    // 清空验证码输入框
    loginForm.captcha = ''
  } catch (error) {
    ElMessage.error('获取验证码失败')
  } finally {
    captchaLoading.value = false
  }
}

// 验证用户名（只校验必填）
const validateUsername = async () => {
  if (!loginFormRef.value) return
  
  try {
    await loginFormRef.value.validateField('username')
    // 验证通过后，焦点移到密码框
    nextTick(() => {
      const passwordInput = document.querySelector('input[type="password"]') as HTMLInputElement
      if (passwordInput) {
        passwordInput.focus()
      }
    })
  } catch (error) {
    // 验证失败，显示错误信息
    console.log('用户名验证失败')
  }
}

// 验证密码（只校验必填）
const validatePassword = async () => {
  if (!loginFormRef.value) return
  
  try {
    await loginFormRef.value.validateField('password')
    // 验证通过后，根据验证码配置决定焦点位置
    nextTick(() => {
      if (isCaptchaEnabled.value) {
        // 启用验证码时，焦点移到验证码框
        const captchaInput = document.querySelector('input[placeholder="请输入验证码"]') as HTMLInputElement
        if (captchaInput) {
          captchaInput.focus()
        }
      } else {
        // 禁用验证码时，直接触发登录
        handleLogin()
      }
    })
  } catch (error) {
    // 验证失败，显示错误信息
    console.log('密码验证失败')
  }
}

// 验证码输入处理
const handleCaptchaInput = (_value: string) => {
  // 只有在启用验证码时才处理
  if (!isCaptchaEnabled.value) return
  
  // 实时清除之前的错误状态
  if (loginFormRef.value) {
    loginFormRef.value.clearValidate('captcha')
  }
}

// 验证码回车事件处理
const handleCaptchaEnter = async () => {
  if (!loginFormRef.value || !isCaptchaEnabled.value) return
  
  // 检查验证码是否输入完整
  if (loginForm.captcha.length !== captchaLength.value) {
    ElMessage.warning(`验证码长度不足，当前${loginForm.captcha.length}位，需要${captchaLength.value}位`)
    // 不触发验证码刷新，让用户继续输入
    return
  }
  
  // 验证码输入完整，先进行表单验证
  try {
    await loginFormRef.value.validateField('captcha')
    // 验证通过，触发登录
    await handleLogin()
  } catch (error) {
    // 验证码格式错误，不刷新验证码，让用户重新输入
    ElMessage.error('验证码格式错误，请重新输入')
  }
}

// 登录处理
const handleLogin = async () => {
  if (!loginFormRef.value) return
  
  try {
    await loginFormRef.value.validate()
    
    loading.value = true
    
    // 只有在启用验证码时才设置验证码相关参数
    if (isCaptchaEnabled.value) {
      loginForm.captchaKey = captchaKey.value
    } else {
      // 禁用验证码时，清空验证码相关字段
      loginForm.captcha = ''
      loginForm.captchaKey = ''
    }
    
    await userStore.login(loginForm)
    
    // 登录成功，如果选择了记住我，保存用户名
    if (loginForm.rememberMe) {
      localStorage.setItem('remembered_username', loginForm.username)
    }
  } catch (error: unknown) {
    // 获取错误信息
    const errorMessage = (error as { response?: { data?: { message?: string } }; message?: string })?.response?.data?.message || 
                        (error as { message?: string })?.message || 
                        '登录失败，请检查用户名和密码'
    
    // 显示错误信息
    ElMessage.error(errorMessage)
    
    // 清空密码
    loginForm.password = ''
    
    // 只有在启用验证码时才处理验证码相关逻辑
    if (isCaptchaEnabled.value) {
      // 判断是否为验证码相关错误
      const errorMsg = errorMessage.toLowerCase()
      const isCaptchaError = errorMsg.includes('验证码') || errorMsg.includes('captcha')
      
      if (isCaptchaError) {
        // 验证码错误：只清空验证码，保留密码
        loginForm.captcha = ''
        // 根据配置决定是否刷新验证码
        if (refreshOnError.value) {
          setTimeout(() => {
            refreshCaptcha()
          }, 1000)
        }
      } else {
        // 其他错误：清空验证码
        loginForm.captcha = ''
      }
    }
  } finally {
    loading.value = false
  }
}

// 记住我功能
const handleRememberMe = (checked: boolean) => {
  if (checked) {
    // 保存用户名到本地存储
    localStorage.setItem('remembered_username', loginForm.username)
  } else {
    // 清除保存的用户名
    localStorage.removeItem('remembered_username')
  }
}

// 生成不同图形的函数
const shapes = {
  heart: () => {
    const points: THREE.Vector3[] = []
    for (let t = 0; t < Math.PI * 2; t += 0.1) {
      const x = 16 * Math.pow(Math.sin(t), 3)
      const y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t)
      points.push(new THREE.Vector3(x/2, y/2, 0))
    }
    return points
  },
  star: () => {
    const points: THREE.Vector3[] = []
    const spikes = 5
    const outerRadius = 10
    const innerRadius = 5
    for (let i = 0; i < spikes * 2; i++) {
      const radius = i % 2 === 0 ? outerRadius : innerRadius
      const angle = (i / spikes) * Math.PI - Math.PI / 2
      points.push(new THREE.Vector3(
        Math.cos(angle) * radius,
        Math.sin(angle) * radius,
        0
      ))
    }
    return points
  },
  circle: () => {
    const points: THREE.Vector3[] = []
    for (let i = 0; i < 36; i++) {
      const angle = (i / 36) * Math.PI * 2
      points.push(new THREE.Vector3(
        Math.cos(angle) * 10,
        Math.sin(angle) * 10,
        0
      ))
    }
    return points
  },
  square: () => {
    const points: THREE.Vector3[] = []
    const size = 10
    for (let i = 0; i < 4; i++) {
      const angle = (i / 4) * Math.PI * 2
      points.push(new THREE.Vector3(
        Math.cos(angle) * size,
        Math.sin(angle) * size,
        0
      ))
    }
    return points
  },
  helix: () => {
    const points: THREE.Vector3[] = []
    for (let i = 0; i < 50; i++) {
      const angle = (i / 50) * Math.PI * 4
      points.push(new THREE.Vector3(
        Math.cos(angle) * 10,
        Math.sin(angle) * 10,
        (i / 50) * 20 - 10
      ))
    }
    return points
  }
}

// 切换图形模式
const toggleShape = (type: string) => {
  if (currentShapeType === type) {
    formingShape = false
    targetShape = null
    currentShapeType = null
  } else {
    formingShape = true
    targetShape = shapes[type as keyof typeof shapes]()
    currentShapeType = type
  }
}

// 重新分布粒子函数
const redistributeParticles = () => {
  if (!particlesGeometry) return
  
  const positions = particlesGeometry.attributes.position.array as Float32Array
  
  for (let i = 0; i < particlesCount; i++) {
    const i3 = i * 3
    
    // 检查粒子是否聚集在中心区域
    const distanceFromCenter = Math.sqrt(
      positions[i3] * positions[i3] + 
      positions[i3 + 1] * positions[i3 + 1] + 
      positions[i3 + 2] * positions[i3 + 2]
    )
    
    // 如果粒子太靠近中心，重新分布到外围
    if (distanceFromCenter < range * 0.3) {
      const radius = range * (0.6 + Math.random() * 0.4)
      const theta = Math.random() * Math.PI * 2
      const phi = Math.acos(2 * Math.random() - 1)
      
      positions[i3] = radius * Math.sin(phi) * Math.cos(theta)
      positions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta)
      positions[i3 + 2] = radius * Math.cos(phi)
      
      // 重置速度 - 保持较慢的速度
      const speed = initSpeed * (0.3 + Math.random() * 0.7) // 进一步减慢
      velArray[i3] = (Math.random() - 0.5) * speed
      velArray[i3 + 1] = (Math.random() - 0.5) * speed
      velArray[i3 + 2] = (Math.random() - 0.5) * speed
    }
  }
  
  particlesGeometry.attributes.position.needsUpdate = true
}

// 初始化3D粒子系统
const initParticles = () => {
  if (!particlesContainer.value) return

  // 创建场景
  scene = new THREE.Scene()
  camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000)
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setClearColor(0x000000, 0)
  particlesContainer.value.appendChild(renderer.domElement)

  // 初始化粒子位置、速度和颜色 - 改进分布算法
  for (let i = 0; i < particlesCount; i++) {
    const i3 = i * 3
    
    // 使用球面分布而不是立方体分布，避免角落聚集
    const radius = Math.random() * range
    const theta = Math.random() * Math.PI * 2
    const phi = Math.acos(2 * Math.random() - 1)
    
    posArray[i3] = radius * Math.sin(phi) * Math.cos(theta)
    posArray[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta)
    posArray[i3 + 2] = radius * Math.cos(phi)
    
    // 减慢初始速度的随机性
    const speed = initSpeed * (0.3 + Math.random() * 0.7) // 减慢速度范围
    velArray[i3] = (Math.random() - 0.5) * speed
    velArray[i3 + 1] = (Math.random() - 0.5) * speed
    velArray[i3 + 2] = (Math.random() - 0.5) * speed
    
    // 初始化固定白色
    colorArray[i3] = 1.0 // R
    colorArray[i3 + 1] = 1.0 // G
    colorArray[i3 + 2] = 1.0 // B
  }

  // 创建粒子几何体
  particlesGeometry = new THREE.BufferGeometry()
  particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3))
  particlesGeometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3))

  // 粒子材质 - 使用简单的PointsMaterial避免频闪
  particlesMaterial = new THREE.PointsMaterial({
    size: 1.5,
    color: 0xffffff,
    transparent: true,
    opacity: 0.8,
    sizeAttenuation: false
  })

  // 使用简单的PointsMaterial，不需要大小属性

  particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial)
  scene.add(particlesMesh)

  // 线条材质
  lineMaterial = new THREE.ShaderMaterial({
    uniforms: {
      color1: { value: new THREE.Color(0xffffff) },
      color2: { value: new THREE.Color(0xffffff) }
    },
    vertexShader: `
      attribute float lineProgress;
      attribute float lineThickness;
      uniform vec3 color1;
      uniform vec3 color2;
      varying vec3 vColor;
      varying float vThickness;
      void main() {
        vColor = mix(color1, color2, lineProgress);
        vThickness = lineThickness;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `,
    fragmentShader: `
      varying vec3 vColor;
      varying float vThickness;
      void main() {
        gl_FragColor = vec4(vColor, 0.3 + vThickness * 0.4);
      }
    `,
    transparent: true
  })

  camera.position.z = 30
  animate()
}

// 动画循环
const animate = () => {
  animationId = requestAnimationFrame(animate)

  if (!particlesGeometry || !particlesMaterial) return

  const positions = particlesGeometry.attributes.position.array as Float32Array

  // 更新鼠标轨迹
  if (mouseTrail.length > 0) {
    mouseTrail.forEach(p => {
      p.life -= 0.03
    })
    mouseTrail = mouseTrail.filter(p => p.life > 0)
  }

  // 鼠标点击效果衰减
  clickActivity *= 0.95
  explosionPower *= 0.9

  // 更新涟漪
  ripples.forEach(r => {
    r.radius += 0.1
    r.power *= 0.95
  })
  ripples = ripples.filter(r => r.power > 0.01)

  // 更新重力点
  gravityPoints.forEach(g => {
    g.life -= 1
  })
  gravityPoints = gravityPoints.filter(g => g.life > 0)

  // 定期重新分布粒子，防止长时间聚集
  redistributionTimer++
  if (redistributionTimer >= redistributionInterval) {
    redistributeParticles()
    redistributionTimer = 0
  }

  const attractForce = 0.008 // 减慢吸引力
  const attractDecay = 0.2
  const damping = 0.985 // 增加阻尼，让粒子更慢
  const keyboardForce = 0.003 // 减慢键盘力
  const repelForce = 0.012 // 减慢斥力
  const minDistance = 5.0 // 增大最小距离
  const randomForce = 0.002 // 减慢随机力

  for (let i = 0; i < particlesCount; i++) {
    const i3 = i * 3
    const px = positions[i3]
    const py = positions[i3 + 1]
    const pz = positions[i3 + 2]
    
    // 移除生命周期系统，保持粒子稳定
    // 粒子颜色保持固定，避免频闪
    // 颜色在初始化时设置，不再更新

    const dx = mousePos.x - px
    const dy = mousePos.y - py
    const dz = mousePos.z - pz
    const dist = Math.sqrt(dx * dx + dy * dy + dz * dz)

    // 粒子间斥力效果 - 防止聚集
    let nearbyParticles = 0
    for (let j = 0; j < particlesCount; j++) {
      if (i === j) continue
      const j3 = j * 3
      const jx = positions[j3]
      const jy = positions[j3 + 1]
      const jz = positions[j3 + 2]
      
      const pdx = px - jx
      const pdy = py - jy
      const pdz = pz - jz
      const pdist = Math.sqrt(pdx * pdx + pdy * pdy + pdz * pdz)
      
      if (pdist < minDistance && pdist > 0) {
        nearbyParticles++
        const repel = repelForce * (minDistance - pdist) / minDistance
        const normalizedDx = pdx / pdist
        const normalizedDy = pdy / pdist
        const normalizedDz = pdz / pdist
        velArray[i3] += normalizedDx * repel
        velArray[i3 + 1] += normalizedDy * repel
        velArray[i3 + 2] += normalizedDz * repel
      }
    }
    
    // 如果附近粒子太多，增加额外的斥力
    if (nearbyParticles > 2) {
      const extraRepel = repelForce * (nearbyParticles + 1)
      velArray[i3] += (Math.random() - 0.5) * extraRepel
      velArray[i3 + 1] += (Math.random() - 0.5) * extraRepel
      velArray[i3 + 2] += (Math.random() - 0.5) * extraRepel
    }
    
    // 添加随机扰动力，防止粒子陷入局部平衡
    if (Math.random() < 0.1) { // 10%的概率添加随机力
      velArray[i3] += (Math.random() - 0.5) * randomForce
      velArray[i3 + 1] += (Math.random() - 0.5) * randomForce
      velArray[i3 + 2] += (Math.random() - 0.5) * randomForce
    }

    // 鼠标吸引力（点击时增强）
    let force = Math.exp(-dist * attractDecay) * attractForce
    if (mouseDown) {
      force *= (1 + clickActivity * 2)
    }

    velArray[i3] += dx * force
    velArray[i3 + 1] += dy * force
    velArray[i3 + 2] += dz * force

    // 爆炸效果
    if (explosionPower > 0.01) {
      const ex = explosionPos.x - px
      const ey = explosionPos.y - py
      const ez = explosionPos.z - pz
      const edist = Math.sqrt(ex * ex + ey * ey + ez * ez)
      if (edist < 5) {
        const repel = (5 - edist) / 5 * explosionPower
        velArray[i3] -= ex * repel
        velArray[i3 + 1] -= ey * repel
        velArray[i3 + 2] -= ez * repel
      }
    }

    // 涟漪效果
    ripples.forEach(r => {
      const rx = r.pos.x - px
      const ry = r.pos.y - py
      const rz = r.pos.z - pz
      const rdist = Math.sqrt(rx * rx + ry * ry + rz * rz)
      if (Math.abs(rdist - r.radius) < 0.5) {
        const repel = Math.sin((rdist - r.radius) * Math.PI * 2) * r.power
        velArray[i3] -= rx * repel
        velArray[i3 + 1] -= ry * repel
        velArray[i3 + 2] -= rz * repel
      }
    })

    // 重力场效果
    gravityPoints.forEach(g => {
      const gx = g.pos.x - px
      const gy = g.pos.y - py
      const gz = g.pos.z - pz
      const gdist = Math.sqrt(gx * gx + gy * gy + gz * gz) + 1
      const gforce = g.power / (gdist * gdist)
      velArray[i3] += gx * gforce
      velArray[i3 + 1] += gy * gforce
      velArray[i3 + 2] += gz * gforce
    })

    // 键盘控制
    if (keys.ArrowUp) velArray[i3 + 1] += keyboardForce
    if (keys.ArrowDown) velArray[i3 + 1] -= keyboardForce
    if (keys.ArrowLeft) velArray[i3] -= keyboardForce
    if (keys.ArrowRight) velArray[i3] += keyboardForce

    // 粒子连线成图
    if (formingShape && targetShape) {
      const targetIndex = i % targetShape.length
      const tx = targetShape[targetIndex].x
      const ty = targetShape[targetIndex].y
      const tz = targetShape[targetIndex].z
      velArray[i3] += (tx - px) * 0.005
      velArray[i3 + 1] += (ty - py) * 0.005
      velArray[i3 + 2] += (tz - pz) * 0.005
    }

    // 阻尼
    velArray[i3] *= damping
    velArray[i3 + 1] *= damping
    velArray[i3 + 2] *= damping

    // 更新位置
    positions[i3] += velArray[i3]
    positions[i3 + 1] += velArray[i3 + 1]
    positions[i3 + 2] += velArray[i3 + 2]

    // 改进的边界处理 - 使用弹性边界而不是硬边界
    for (let j = 0; j < 3; j++) {
      const pos = positions[i3 + j]
      const vel = velArray[i3 + j]
      
      if (pos > range) {
        positions[i3 + j] = range - (pos - range) * 0.1 // 弹性反弹
        velArray[i3 + j] = -Math.abs(vel) * 0.8 // 反向速度
      } else if (pos < -range) {
        positions[i3 + j] = -range + (-range - pos) * 0.1 // 弹性反弹
        velArray[i3 + j] = Math.abs(vel) * 0.8 // 反向速度
      }
    }

    // 使用简单的PointsMaterial，不需要更新大小
  }

  particlesGeometry.attributes.position.needsUpdate = true
  // 大小和颜色都固定，不需要更新
  // particlesGeometry.attributes.size.needsUpdate = true
  // particlesGeometry.attributes.color.needsUpdate = true

  // 整体旋转
  particlesMesh.rotation.y += 0.0003
  particlesMesh.rotation.x += 0.0001

  // 鼠标连线
  if (mouseActivity > 0) {
    const linesGeometry = new THREE.BufferGeometry()
    const linesArray: number[] = []
    const progressArray: number[] = []
    const thicknessArray: number[] = []

    for (let i = 0; i < particlesCount; i++) {
      const particlePos = new THREE.Vector3(
        positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]
      )
      const distance = mousePos.distanceTo(particlePos)

      if (distance < 8) { // 缩短连线距离
        const thickness = 1 - distance / 8 // 调整厚度计算
        linesArray.push(mousePos.x, mousePos.y, mousePos.z)
        progressArray.push(0)
        thicknessArray.push(thickness)
        
        linesArray.push(particlePos.x, particlePos.y, particlePos.z)
        progressArray.push(1)
        thicknessArray.push(thickness)
      }
    }

    linesGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(linesArray), 3))
    linesGeometry.setAttribute('lineProgress', new THREE.BufferAttribute(new Float32Array(progressArray), 1))
    linesGeometry.setAttribute('lineThickness', new THREE.BufferAttribute(new Float32Array(thicknessArray), 1))

    const linesMesh = new THREE.LineSegments(linesGeometry, lineMaterial)
    scene.add(linesMesh)
    renderer.render(scene, camera)
    scene.remove(linesMesh)

    mouseActivity -= 0.01
  } else {
    renderer.render(scene, camera)
  }
}

// 事件监听器
const setupEventListeners = () => {
  // 鼠标移动事件
  const handleMouseMove = (e: MouseEvent) => {
    const mouseNDC = new THREE.Vector3(
      (e.clientX / window.innerWidth) * 2 - 1,
      -(e.clientY / window.innerHeight) * 2 + 1,
      0.5
    )
    mouseNDC.unproject(camera)
    const dir = mouseNDC.sub(camera.position).normalize()
    const distance = -camera.position.z / dir.z
    mousePos = camera.position.clone().add(dir.multiplyScalar(distance))
    mouseActivity = 1

    // 添加鼠标轨迹
    mouseTrail.push({pos: mousePos.clone(), life: 1})
    if (mouseTrail.length > 30) mouseTrail.shift()

    // 拖拽交互
    if (dragStart) {
      const deltaX = e.clientX - lastMousePos.x
      const deltaY = e.clientY - lastMousePos.y
      const positions = particlesGeometry.attributes.position.array as Float32Array
      for (let i = 0; i < particlesCount; i++) {
        positions[i * 3] += deltaX * 0.01
        positions[i * 3 + 1] -= deltaY * 0.01
      }
      particlesGeometry.attributes.position.needsUpdate = true
    }
    
    lastMousePos.set(e.clientX, e.clientY)
  }

  // 鼠标按下事件
  const handleMouseDown = (e: MouseEvent) => {
    mouseDown = true
    clickActivity = 1
    dragStart = true
    
    // 记录爆炸位置
    const mouseNDC = new THREE.Vector3(
      (e.clientX / window.innerWidth) * 2 - 1,
      -(e.clientY / window.innerHeight) * 2 + 1,
      0.5
    )
    mouseNDC.unproject(camera)
    const dir = mouseNDC.sub(camera.position).normalize()
    const distance = -camera.position.z / dir.z
    explosionPos = camera.position.clone().add(dir.multiplyScalar(distance))
    explosionPower = 0.5

    // 添加涟漪效果
    ripples.push({pos: explosionPos.clone(), power: 1, radius: 0})

    // 添加重力点
    gravityPoints.push({pos: explosionPos.clone(), power: 0.02, life: 100})
  }

  // 鼠标抬起事件
  const handleMouseUp = () => {
    mouseDown = false
    dragStart = false
  }

  // 键盘事件
  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.key in keys) {
      keys[e.key as keyof typeof keys] = true
    }
    // 按不同键切换不同图形
    if (e.key === 'g' || e.key === 'G') toggleShape('heart')
    if (e.key === 's' || e.key === 'S') toggleShape('star')
    if (e.key === 'c' || e.key === 'C') toggleShape('circle')
    if (e.key === 'b' || e.key === 'B') toggleShape('square')
    if (e.key === 'h' || e.key === 'H') toggleShape('helix')
  }
  
  const handleKeyUp = (e: KeyboardEvent) => {
    if (e.key in keys) {
      keys[e.key as keyof typeof keys] = false
    }
  }

  // 窗口大小调整
  const handleResize = () => {
    if (camera && renderer) {
      camera.aspect = window.innerWidth / window.innerHeight
      camera.updateProjectionMatrix()
      renderer.setSize(window.innerWidth, window.innerHeight)
    }
  }

  // 触控事件处理
  const handleTouchMove = (e: TouchEvent) => {
    e.preventDefault()
    if (e.touches.length > 0) {
      const touch = e.touches[0]
      const mouseEvent = new MouseEvent('mousemove', {
        clientX: touch.clientX,
        clientY: touch.clientY
      })
      handleMouseMove(mouseEvent)
    }
  }

  const handleTouchStart = (e: TouchEvent) => {
    e.preventDefault()
    if (e.touches.length > 0) {
      const touch = e.touches[0]
      const mouseEvent = new MouseEvent('mousedown', {
        clientX: touch.clientX,
        clientY: touch.clientY
      })
      handleMouseDown(mouseEvent)
    }
  }

  const handleTouchEnd = (e: TouchEvent) => {
    e.preventDefault()
    handleMouseUp()
  }

  // 添加事件监听器
  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mousedown', handleMouseDown)
  document.addEventListener('mouseup', handleMouseUp)
  document.addEventListener('touchmove', handleTouchMove, { passive: false })
  document.addEventListener('touchstart', handleTouchStart, { passive: false })
  document.addEventListener('touchend', handleTouchEnd, { passive: false })
  document.addEventListener('keydown', handleKeyDown)
  document.addEventListener('keyup', handleKeyUp)
  window.addEventListener('resize', handleResize)

  // 返回清理函数
  return () => {
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mousedown', handleMouseDown)
    document.removeEventListener('mouseup', handleMouseUp)
    document.removeEventListener('touchmove', handleTouchMove)
    document.removeEventListener('touchstart', handleTouchStart)
    document.removeEventListener('touchend', handleTouchEnd)
    document.removeEventListener('keydown', handleKeyDown)
    document.removeEventListener('keyup', handleKeyUp)
    window.removeEventListener('resize', handleResize)
  }
}

// 输入框焦点处理
const handleInputFocus = (event: FocusEvent) => {
  const inputWrapper = (event.target as HTMLElement).closest('.input-wrapper')
  if (inputWrapper) {
    inputWrapper.classList.add('focused')
  }
}

const handleInputBlur = (event: FocusEvent) => {
  const inputWrapper = (event.target as HTMLElement).closest('.input-wrapper')
  if (inputWrapper) {
    inputWrapper.classList.remove('focused')
  }
}

// 键盘事件处理（已移除，改为在每个输入框上单独处理）

let cleanupEventListeners: (() => void) | null = null

onMounted(() => {
  // 检查是否已经登录
  if (userStore.isLoggedIn) {
    ElMessage.info('您已登录，正在跳转...')
    router.push('/')
    return
  }
  
  // 只有在启用验证码时才获取验证码
  if (isCaptchaEnabled.value) {
    refreshCaptcha()
  }
  
  // 检查是否有记住的用户名
  const rememberedUsername = localStorage.getItem('remembered_username')
  if (rememberedUsername) {
    loginForm.username = rememberedUsername  // 修改为与后端一致
    loginForm.rememberMe = true
  }
  
  // 初始化3D粒子系统
  nextTick(() => {
    initParticles()
    cleanupEventListeners = setupEventListeners()
  })
})

// 组件卸载时清理
onUnmounted(() => {
  // 清理事件监听器
  if (cleanupEventListeners) {
    cleanupEventListeners()
  }
  
  // 清理Three.js资源
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
  
  if (renderer) {
    renderer.dispose()
  }
  
  if (particlesGeometry) {
    particlesGeometry.dispose()
  }
  
  if (particlesMaterial) {
    particlesMaterial.dispose()
  }
  
  if (lineMaterial) {
    lineMaterial.dispose()
  }
})
</script>

<style lang="scss" scoped>
.login-container {
  min-height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 20px;
  position: relative;
  overflow: hidden;
}

// 3D粒子背景
.particles-background {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: auto;
  
  canvas {
    display: block;
    width: 100%;
    height: 100%;
  }
}


.login-box {
  width: 100%;
  max-width: 400px;
  background: rgba(255, 255, 255, 0.95);
  border-radius: 20px;
  padding: 40px;
  box-shadow: 
    0 20px 40px rgba(0, 0, 0, 0.1),
    0 0 0 1px rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(20px);
  position: relative;
  z-index: 1;
  transition: all 0.3s ease;
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 
      0 25px 50px rgba(0, 0, 0, 0.15),
      0 0 0 1px rgba(255, 255, 255, 0.3);
  }
}

.login-header {
  text-align: center;
  margin-bottom: 32px;
  position: relative;
}

.logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 8px;
  
  .logo-icon {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    border-radius: 12px;
    box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
    transition: all 0.3s ease;
    
    &:hover {
      transform: scale(1.1) rotate(5deg);
      box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
    }
    
    img {
      width: 30px;
      height: 30px;
      filter: brightness(0) invert(1);
    }
  }
  
  h1 {
    margin: 0;
    font-size: 28px;
    font-weight: bold;
    background: linear-gradient(135deg, #667eea, #764ba2);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    animation: textShine 3s ease-in-out infinite;
  }
}

@keyframes textShine {
  0%, 100% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
}

.subtitle {
  margin: 0;
  color: #666;
  font-size: 14px;
  font-weight: 500;
}

.header-line {
  width: 60px;
  height: 3px;
  background: linear-gradient(90deg, #667eea, #764ba2);
  margin: 16px auto 0;
  border-radius: 2px;
  animation: lineExpand 2s ease-out;
}

@keyframes lineExpand {
  0% {
    width: 0;
  }
  100% {
    width: 60px;
  }
}

.login-form {
  .el-form-item {
    margin-bottom: 24px;
    
    &.form-item-enhanced {
      position: relative;
      
      .input-wrapper {
        position: relative;
        
        .input-line {
          position: absolute;
          bottom: 0;
          left: 0;
          width: 0;
          height: 2px;
          background: linear-gradient(90deg, #667eea, #764ba2);
          transition: width 0.3s ease;
          border-radius: 1px;
        }
        
        &.focused .input-line {
          width: 100%;
        }
        
        :deep(.el-input__wrapper) {
          border-radius: 12px;
          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
          transition: all 0.3s ease;
          border: 2px solid transparent;
          
          &:hover {
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
          }
          
          &.is-focus {
            border-color: #667eea;
            box-shadow: 0 4px 16px rgba(102, 126, 234, 0.2);
          }
        }
        
        :deep(.el-input__inner) {
          font-size: 16px;
          padding: 12px 16px;
        }
        
        :deep(.el-input__prefix) {
          color: #667eea;
        }
      }
    }
  }
}

.captcha-container {
  display: flex;
  gap: 12px;
  align-items: flex-end;
  
  .input-wrapper {
    flex: 1;
  }
}

.captcha-image {
  width: 100px;
  height: 40px;
  border: 2px solid #e4e7ed;
  border-radius: 12px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #f8f9fa, #e9ecef);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
  
  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(102, 126, 234, 0.1), transparent);
    transition: left 0.5s ease;
  }
  
  &:hover {
    border-color: #667eea;
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
    
    &::before {
      left: 100%;
    }
  }
  
  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 10px;
  }
  
  span {
    font-size: 12px;
    color: #999;
    font-weight: 500;
  }
  
  .el-icon {
    font-size: 16px;
    color: #667eea;
    animation: rotate 2s linear infinite;
  }
  
  .is-loading {
    animation: pulse 1.5s ease-in-out infinite;
  }
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

.login-options {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin-bottom: 8px;
  
  :deep(.el-checkbox) {
    .el-checkbox__label {
      color: #666;
      font-weight: 500;
    }
    
    .el-checkbox__input.is-checked .el-checkbox__inner {
      background-color: #667eea;
      border-color: #667eea;
    }
  }
  
  :deep(.el-link) {
    color: #667eea;
    font-weight: 500;
    
    &:hover {
      color: #764ba2;
    }
  }
}

.button-wrapper {
  position: relative;
  width: 100%;
}

.login-button {
  width: 100%;
  height: 48px;
  font-size: 16px;
  font-weight: 600;
  border-radius: 12px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  border: none;
  position: relative;
  overflow: hidden;
  transition: all 0.3s ease;
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
  }
  
  &:active {
    transform: translateY(0);
  }
  
  .button-text {
    position: relative;
    z-index: 2;
    color: white;
  }
  
  .button-shine {
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
    transition: left 0.6s ease;
  }
  
  &:hover .button-shine {
    left: 100%;
  }
  
  // 加载状态
  &.is-loading {
    .button-text {
      opacity: 0.8;
    }
  }
}

.login-footer {
  margin-top: 32px;
  text-align: center;
  
  p {
    margin: 0;
    color: rgba(255, 255, 255, 0.8);
    font-size: 14px;
    font-weight: 500;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  }
}

// 页面进入动画
.login-container {
  animation: fadeInUp 0.8s ease-out;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

// 登录框进入动画
.login-box {
  animation: slideInUp 0.6s ease-out 0.2s both;
}

@keyframes slideInUp {
  from {
    opacity: 0;
    transform: translateY(50px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

// 输入框进入动画
.form-item-enhanced {
  animation: fadeInLeft 0.5s ease-out;
  animation-fill-mode: both;
  
  &:nth-child(1) { animation-delay: 0.3s; }
  &:nth-child(2) { animation-delay: 0.4s; }
  &:nth-child(3) { animation-delay: 0.5s; }
  &:nth-child(4) { animation-delay: 0.6s; }
  &:nth-child(5) { animation-delay: 0.7s; }
}

@keyframes fadeInLeft {
  from {
    opacity: 0;
    transform: translateX(-30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

// 响应式设计
@media (max-width: 480px) {
  .login-box {
    padding: 24px;
    margin: 0 16px;
    border-radius: 16px;
  }
  
  .logo h1 {
    font-size: 24px;
  }
  
  .logo-icon {
    width: 40px;
    height: 40px;
    
    img {
      width: 24px;
      height: 24px;
    }
  }
  
  .particles-background {
    // 移动端优化粒子效果
    canvas {
      opacity: 0.8;
    }
  }
}

@media (max-width: 360px) {
  .login-box {
    padding: 20px;
    margin: 0 12px;
  }
  
  .captcha-container {
    flex-direction: column;
    gap: 8px;
    
    .captcha-image {
      width: 100%;
      height: 44px;
    }
  }
}
</style>
